package com.naiterui.common.jinshuju.callback.router;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.naiterui.common.jinshuju.bean.JinshujuCallback;
import com.naiterui.common.jinshuju.bean.JinshujuInfo;
import com.naiterui.common.jinshuju.service.JinshujuService;

/**
 * 金数据回调路由，通过配置，将公共金数据回调路由至各业务处理
 */
public class CallbackRouter {

    private static final int DEFAULT_THREAD_POOL_SIZE = 100;
    private final Logger log = LoggerFactory.getLogger(CallbackRouter.class);
    private final List<CallbackRouterRule> rules = new ArrayList<>();

    private final JinshujuService jinshujuService;
    private ExecutorService executorService;

    public CallbackRouter(JinshujuService jinshujuService) {
        this.jinshujuService = jinshujuService;
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("JinshujuCallbackRouter-pool-%d").build();
        this.executorService = new ThreadPoolExecutor(DEFAULT_THREAD_POOL_SIZE, DEFAULT_THREAD_POOL_SIZE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), namedThreadFactory);
    }

    public void shutDownExecutorService() {
        this.executorService.shutdown();
    }

    public void setExecutorService(ExecutorService executorService) {
        this.executorService = executorService;
    }

    List<CallbackRouterRule> getRules() {
        return this.rules;
    }

    public CallbackRouterRule rule() {
        return new CallbackRouterRule(this);
    }

    public JinshujuInfo route(final JinshujuCallback jinshujuCallback, final Map<String, Object> context) {
        return this.route(jinshujuCallback, context, null);
    }

    public JinshujuInfo route(final JinshujuCallback jinshujuCallback, final Map<String, Object> context, JinshujuService jinshujuService) {
        if (jinshujuService == null) {
            jinshujuService = this.jinshujuService;
        }
        final JinshujuService fJinshujuService = jinshujuService;

        /*if (this.isMsgDuplicated(jinshujuEntry)) {
            // 如果是重复消息，那么就不做处理
            return null;
        }*/

        final List<CallbackRouterRule> matchRules = new ArrayList<>();
        // 收集匹配的规则
        for (final CallbackRouterRule rule : this.rules) {
            if (rule.test(jinshujuCallback)) {
                matchRules.add(rule);
                if (!rule.isReEnter()) {
                    break;
                }
            }
        }

        if (matchRules.size() == 0) {
            return null;
        }

        JinshujuInfo res = null;
        final List<Future<?>> futures = new ArrayList<>();
        for (final CallbackRouterRule rule : matchRules) {
            // 返回最后一个非异步的rule的执行结果
            if (rule.isAsync()) {
                futures.add(
                        this.executorService.submit(() -> {
                            rule.service(jinshujuCallback, context, fJinshujuService);
                        })
                );
            } else {
                res = rule.service(jinshujuCallback, context, jinshujuService);
            }
        }

        if (futures.size() > 0) {
            this.executorService.submit(() -> {
                for (Future<?> future : futures) {
                    try {
                        future.get();
                        //异步执行结束
                    } catch (InterruptedException e) {
                        CallbackRouter.this.log.error("Error happened when wait task finish", e);
                        Thread.currentThread().interrupt();
                    } catch (ExecutionException e) {
                        CallbackRouter.this.log.error("Error happened when wait task finish", e);
                    }
                }
            });
        }
        return res;
    }

    public JinshujuInfo route(final JinshujuCallback jinshujuCallback) {
        return this.route(jinshujuCallback, new HashMap<String, Object>());
    }

}
